export function initEvents (vm) {
    vm._events = Object.create(null)
    // 初始化父组件附加的事件
    const listeners = vm.$options._parentListeners
    if (listeners) {
        updateComponentListeners(vm, listeners)
    }
}

let target

function add(event, fn, once) {
    if (once) {
        target.$once(event, fn)
    } else {
        target.$on(event, fn)
    }
}

function remove (event, fn) {
    target.$off(event, fn)
}

export function updateComponentListeners(vm, listeners, oldListeners) {
    target = vm
    updateListeners(listeners, oldListeners || {}, add, remove, vm)
}

export function updateListeners(on, oldOn, add, remove, vm) {
    let name, cur, old, event
    for (name in on) {
        cur = on[name]
        old = oldOn[name]
        event = normalizeEvent(name)
        // 判断事件名对应的值是否是undefined或null，如果是，触发控制台警告
        if (isUndef(cur)) {
            process.env.NODE_ENV !== 'production' && warn(
                `Invalid handler for event "${event.name}": got ` + String(cur),
                vm
            )
        } else if (isUndef(old)) {
            // 判断事件名在oldOn中是否存在，如果不存在，则调用add注册事件
            if (isUndef(cur.fns)) {
                cur = on[name] = createFnInvoker(cur)
            }
            add(event.name, cur, event.once, event.capture, event.passive)
        } else if (cur !== old) {
            // 如果事件名在on和oldOn都存在但是不相同，则将事件回调替换成on中回调，
            // 并且把on中的回调引用指向真实的事件系统中的事件，也就是oldOn中对应的事件
            old.fns = cur
            on[name] = old
        }
    }
    for (name in oldOn) {
        if (isUndef(on[name])) {
            event = normalizeEvent(name)
            remove(event.name, oldOn[name], event.capture)
        }
    }
}

function normalizeEvent (name) {
    const passive = name.chartAt(0) === '&'
    name = passive ? name.slice(1) : name
    const once = name.chartAt(0) === '~'
    name = once ? name.slice(1) : name
    const capture = name.chartAt(0) === '!'
    name = capture ? name.slice(1) : name
    return {
        name,
        once,
        capture,
        passive
    }
}

function createFnInvoker (fns) {
    function invoker() {
        const fns = invoker.fns
        if (Array.isArray(fns)) {
            const cloned = fns.slice()
            for (let i = 0; i < cloned.length; i++) {
                cloned[i].apply(null, arguments)
            }
        } else {
            return fns.apply(null, arguments)
        }
    }
    invoker.fns = fns
    return invoker
}